package com.example.utils;

import cn.hutool.log.StaticLog;
import org.gdal.gdal.gdal;
import org.gdal.ogr.*;
import org.geotools.data.DataStore;
import org.geotools.data.DataStoreFinder;
import org.geotools.data.collection.ListFeatureCollection;
import org.geotools.data.simple.SimpleFeatureCollection;
import org.geotools.data.simple.SimpleFeatureStore;
import org.geotools.feature.simple.SimpleFeatureBuilder;
import org.geotools.feature.simple.SimpleFeatureTypeBuilder;
import org.geotools.geojson.geom.GeometryJSON;
import org.geotools.referencing.CRS;
import org.locationtech.jts.geom.Geometry;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.referencing.FactoryException;

import java.io.IOException;
import java.sql.Date;
import java.sql.Time;
import java.sql.Timestamp;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class GdalUtil {
    static {
        gdal.AllRegister();//设置gdal环境
        // 为了支持中文路径，请添加下面这句代码
        gdal.SetConfigOption("GDAL_FILENAME_IS_UTF8","YES");
        // 为了使属性表字段支持中文，请添加下面这句
        gdal.SetConfigOption("SHAPE_ENCODING","");
    }

    //默认的geom字段
    private static String GEOM_NAME = "geom";

    //数据库连接信息
    private static Map DBMAP = new HashMap<String, Object>() {
        {
            put("dbtype", "postgis");
            put("host", "192.168.3.2");
            put("port", "54342");
            put("schema", "public");
            put("database", "engine");
            put("user", "postgres");
            put("passwd", "postgres");
        }
    };

    public static DataSource GetDataSourceByGdbFilePath(String filePath) {
        Driver driver = ogr.GetDriverByName("OpenFileGDB"); //设置驱动
        DataSource dataSource = driver.Open(filePath, 0);
        return dataSource;
    }

    /**
     * 将GDB数据导入到数据库中，只会导入GDB中的矢量数,保留现存的空间坐标系信息
     *
     * @param gdbFilePath gdb路径
     */
    public static void importGdbToDatabase(String gdbFilePath) {
        Object baseName = null;
        if (baseName != null) {//创建库
            DBMAP.put("database", baseName);
        }
        DataSource dataSource = GetDataSourceByGdbFilePath(gdbFilePath);
        for (int i = 0; i < dataSource.GetLayerCount(); i++) {
            Layer layer = dataSource.GetLayer(i);
            String wkt = layer.GetSpatialRef().ExportToWkt();//坐标系信息
            String tableName = layer.GetName();//获取表名称
            SimpleFeatureType simpleFeatureType = null;
            List<SimpleFeature> features = new ArrayList<>();
            DataStore dataStore = null;
            do {//获取图层下的要素
                Feature feature = layer.GetNextFeature();
                if (null == feature) {
                    break;
                }
                //创建表数据类型
                if (simpleFeatureType == null) {
                    simpleFeatureType = creatSimpleFeatureType(feature, wkt, tableName);
                    dataStore = createTable(simpleFeatureType);
                }
                SimpleFeatureBuilder featureBuilder = new SimpleFeatureBuilder(simpleFeatureType);
                //将Feature对象添加到集合中去
                addFeature(features, feature, featureBuilder);
            } while (true);

            //将组装的数据插入数据库中
            try {
                SimpleFeatureCollection simpleFeatureCollection = new ListFeatureCollection(simpleFeatureType, features);
                SimpleFeatureStore featureStore = (SimpleFeatureStore) dataStore.getFeatureSource(tableName);
                featureStore.addFeatures(simpleFeatureCollection);
                dataStore.dispose();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 将GDBFeature 添加到geotools集合中
     *
     * @param features
     * @param feature
     * @param featureBuilder
     */
    private static void addFeature(List<SimpleFeature> features, Feature feature, SimpleFeatureBuilder featureBuilder) {
        //添加矢量数据
        GeometryJSON geometryJSON = new GeometryJSON();
        try {
            featureBuilder.add(geometryJSON.read(feature.GetGeometryRef().ExportToJson()));
        } catch (IOException e) {
            e.printStackTrace();
        }
        //添加属性数据
        for (int p = 0; p < feature.GetFieldCount(); p++) {
            String data = feature.GetFieldAsString(feature.GetFieldDefnRef(p).GetName());
            featureBuilder.add(data);
        }
        SimpleFeature f = featureBuilder.buildFeature(null);
        features.add(f);
    }

//    private static Object getData(Feature feature,FieldDefn fieldDefn){
//        int i = fieldDefn.GetFieldType();
//        Object data = null;
//        switch (i){
//            case  ogr.OFTString:
//                feature.GetFieldAsString(fieldDefn.GetName());
//            case  ogr.OFTReal:
//                feature.GetFieldAsDouble(fieldDefn.GetName());
//        }
//        return data;
//    }

    /**
     * 创建FeatureType(表结构)
     */
    public static SimpleFeatureType creatSimpleFeatureType(Feature feature, String wkt, String tableName) {
        SimpleFeatureTypeBuilder builder = new SimpleFeatureTypeBuilder();
        builder.setName(tableName);
        try {
            builder.setCRS(CRS.parseWKT(wkt));
        } catch (FactoryException e) {
            e.printStackTrace();
        }
        //添加矢量数据类型
        GeometryJSON geometryJSON = new GeometryJSON();
        try {
            Geometry read = geometryJSON.read(feature.GetGeometryRef().ExportToJson());
            Class geomClass = Class.forName("org.locationtech.jts.geom." + read.getGeometryType());
            builder.add(GEOM_NAME, geomClass);
        } catch (Exception e) {
            e.printStackTrace();
        }

        //添加属性
        for (int p = 0; p < feature.GetFieldCount(); p++) {
            builder.add(feature.GetFieldDefnRef(p).GetName(), getProperty(feature, p).getClass());
        }

        // build the type
        final SimpleFeatureType LOCATION = builder.buildFeatureType();
        return LOCATION;
    }

    /**
     * 创建表
     */
    public static DataStore createTable(SimpleFeatureType simpleFeatureType) {
        DataStore dataStore = null;
        try {
            dataStore = DataStoreFinder.getDataStore(DBMAP);
            dataStore.createSchema(simpleFeatureType);//创建表
        } catch (IOException e) {
            e.printStackTrace();
            StaticLog.error("创建表出错");
        }
        return dataStore;
    }


    /**
     * @param feature
     * @param index
     * @return
     */
    private static Object getProperty(Feature feature, int index) {
        int type = feature.GetFieldType(index);
        GdalUtil.PropertyGetter propertyGetter;
        if (type < 0 || type >= propertyGetters.length) {
            propertyGetter = stringPropertyGetter;
        } else {
            propertyGetter = propertyGetters[type];
        }
        try {
            return propertyGetter.get(feature, index);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 属性获取器
     */
    @FunctionalInterface
    private interface PropertyGetter {
        Object get(Feature feature, int index);
    }

    private static final GdalUtil.PropertyGetter stringPropertyGetter = (feature, index) -> feature.GetFieldAsString(index);

    /**
     * feature.GetFieldType(index)得到一个属性类型的int值,该值对应具体类型
     */
    private static final GdalUtil.PropertyGetter[] propertyGetters = new GdalUtil.PropertyGetter[]
            {
                    (feature, index) -> feature.GetFieldAsInteger(index),//0	Integer
                    (feature, index) -> feature.GetFieldAsIntegerList(index),//1	IntegerList
                    (feature, index) -> feature.GetFieldAsDouble(index),//2	Real
                    (feature, index) -> feature.GetFieldAsDoubleList(index),//3	RealList
                    stringPropertyGetter,//4	String
                    (feature, index) -> feature.GetFieldAsStringList(index),//5	StringList
                    stringPropertyGetter,//6	(unknown)
                    stringPropertyGetter,//7	(unknown)
                    (feature, index) -> feature.GetFieldAsBinary(index),//8	Binary
                    (feature, index) -> {
                        int[] pnYear = new int[1];
                        int[] pnMonth = new int[1];
                        int[] pnDay = new int[1];
                        int[] pnHour = new int[1];
                        int[] pnMinute = new int[1];
                        float[] pfSecond = new float[1];
                        int[] pnTZFlag = new int[1];
                        feature.GetFieldAsDateTime(index, pnYear, pnMonth, pnDay, pnHour, pnMinute, pfSecond, pnTZFlag);
                        Date date = Date.valueOf(LocalDate.of(pnYear[0], pnMonth[0], pnDay[0]));
                        return date;
                    },//9	Date
                    (feature, index) -> {
                        int[] pnYear = new int[1];
                        int[] pnMonth = new int[1];
                        int[] pnDay = new int[1];
                        int[] pnHour = new int[1];
                        int[] pnMinute = new int[1];
                        float[] pfSecond = new float[1];
                        int[] pnTZFlag = new int[1];
                        feature.GetFieldAsDateTime(index, pnYear, pnMonth, pnDay, pnHour, pnMinute, pfSecond, pnTZFlag);
                        float fSecond = pfSecond[0];
                        int s = (int) fSecond;
                        int ns = (int) (1000000000 * fSecond - s);
                        Time time = Time.valueOf(LocalTime.of(pnHour[0], pnMinute[0], s, ns));
                        return time;
                    },// 10	Time
                    (feature, index) -> {
                        int[] pnYear = new int[1];
                        int[] pnMonth = new int[1];
                        int[] pnDay = new int[1];
                        int[] pnHour = new int[1];
                        int[] pnMinute = new int[1];
                        float[] pfSecond = new float[1];
                        int[] pnTZFlag = new int[1];
                        feature.GetFieldAsDateTime(index, pnYear, pnMonth, pnDay, pnHour, pnMinute, pfSecond, pnTZFlag);
                        float fSecond = pfSecond[0];
                        int s = (int) fSecond;
                        int ns = (int) (1000000000 * fSecond - s);
                        LocalDateTime localDateTime = LocalDateTime.of(
                                LocalDate.of(pnYear[0], pnMonth[0], pnDay[0]),
                                LocalTime.of(pnHour[0], pnMinute[0], s, ns)
                        );
                        Timestamp timestamp = Timestamp.valueOf(localDateTime);
                        return timestamp;
                    },//11	DateTime
                    (feature, index) -> feature.GetFieldAsInteger64(index),//12	Integer64
                    (feature, index) -> feature.GetFieldAsIntegerList(index),//13 Integer64List
            };
}
